﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics.Contracts;

namespace ReactiveCode.Util.Expressions
{
    static class ExpressionUtil
    {
        public static Expression<Func<TResult>> MakeReturnType<TSource, TResult>(this Expression<Func<TSource>> lambda)
        {
            Contract.Requires(lambda != null);
            Contract.Ensures(Contract.Result<Expression<Func<TResult>>>() != null);

            if (typeof(TSource) == typeof(TResult))
                return lambda as Expression<Func<TResult>>;
            return Expression.Lambda<Func<TResult>>(Expression.Convert(lambda.Body, typeof(TResult)), lambda.Parameters.ToArray());
        }
        public static Expression StripConversion(this Expression expr)
        {
            Contract.Requires(expr != null);
            while (expr.NodeType == ExpressionType.Convert || expr.NodeType == ExpressionType.ConvertChecked)
                expr = (expr as UnaryExpression).Operand;
            return expr;
        }
        public static Func<T> Compile<T>(Expression expr)
        {
            Contract.Assert(expr != null);
            var cnst = expr.StripConversion() as ConstantExpression;
            if (cnst != null)
                return () => (T)cnst.Value;

            if (expr.Type != typeof(T))
                expr = Expression.Convert(expr, typeof(T));
            return Expression.Lambda<Func<T>>(expr).Compile();
        }
        public static Expression Replace(this Expression expression, Expression what, Expression with)
        {
            return new ExpressionReplacer().Replace(expression, what, with);
        }
    }
}
